/*
 * Tencent is pleased to support the open source community by making 蓝鲸 available.
 * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.
 * Licensed under the MIT License (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 * http://opensource.org/licenses/MIT
 * Unless required by applicable law or agreed to in writing, software distributed under
 * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied. See the License for the specific language governing permissions and
 * limitations under the License.
 */

package auth

import (
	"encoding/json"
	"net/http"
	"strings"

	"configcenter/src/common"
	"configcenter/src/common/blog"
	"configcenter/src/common/util"
	"configcenter/src/web_server/middleware/types"

	"github.com/gin-gonic/gin"
	"github.com/holmeswang/contrib/sessions"
)

type publicAuth struct {
}

// ValidResAccess valid resource access privilege
func (m *publicAuth) ValidResAccess(pathArr []string, c *gin.Context) bool {
	rid := util.GetHTTPCCRequestID(c.Request.Header)
	ctx := util.NewContextFromGinContext(c)
	var userName string
	session := sessions.Default(c)
	role := session.Get("role")
	pathStr := c.Request.URL.Path
	method := c.Request.Method

	// admin have full privilege
	if nil != role {
		irole := role.(string)
		if "1" == irole {
			return true
		}
	}
	iuserName := session.Get(common.WEBSessionUinKey)
	if nil == iuserName {
		blog.Errorf("user name error, rid: %s", rid)
		return false
	}

	userName = iuserName.(string)

	// index page or static page
	if 0 == len(pathArr) || "" == pathArr[1] || "static" == pathArr[1] {
		return true
	}

	// valid privilege url must match session
	if strings.Contains(pathStr, types.BK_CC_PRIVI_PATTERN) {
		if pathArr[len(pathArr)-1] == userName {
			return true
		}
		blog.Errorf("privilege user name error, rid: %s", rid)
		return false
	}
	// search classfication return true
	if strings.Contains(pathStr, types.BK_CC_CLASSIFIC) && method == common.HTTPSelectPost {
		return true
	}

	// search object attr  return true
	if strings.Contains(pathStr, types.BK_CC_OBJECT_ATTR) && method == common.HTTPSelectPost {
		return true
	}

	// usercustom return true
	if strings.Contains(pathStr, types.BK_CC_USER_CUSTOM) {
		return true
	}

	// objectatt group return true
	if strings.Contains(pathStr, types.BK_OBJECT_ATT_GROUP) {
		return true
	}

	// favorites return true
	if strings.Contains(pathStr, types.BK_CC_HOST_FAVORITES) {
		return true
	}

	// association kind search, but not batch
	if strings.Contains(pathStr, types.BK_TOPO_ASSOCIATION_KIND_SEARCH) &&
		!strings.Contains(pathStr, "batch") && method == http.MethodPost {
		return true
	}

	// search inst association
	if types.SearchInstAssociation == pathStr && method == http.MethodPost {
		return true
	}

	// batch search topo association kind
	if strings.HasSuffix(pathStr, types.BKTopoBatchSearchAssociationKind) && method == http.MethodPost {
		return true
	}

	// get processes
	if types.ProcSearchRegexp.MatchString(pathStr) && method == http.MethodPost {
		return true
	}

	// get user api
	if types.SearchUserAPIRegexp.MatchString(pathStr) && method == http.MethodPost {
		return true
	}

	// get association kind between objects
	if pathStr == types.SearchObjectAssociation && method == http.MethodPost {
		return true
	}

	// get association kind between objects
	if pathStr == types.CreateInstAssociation && method == http.MethodPost {
		return true
	}

	// get objects
	if pathStr == types.SearchObjects && method == http.MethodPost {
		return true
	}

	// export objects excel
	if types.ExportObjectExcelRegexp.MatchString(pathStr) && method == http.MethodPost {
		return true
	}

	if types.DeleteInstAssociationRegex.MatchString(pathStr) && method == http.MethodDelete {
		return true
	}

	// export business hosts
	if pathStr == types.ExportHosts && method == http.MethodPost {
		return true
	}

	// search uniques info for a object.
	if types.SearchObjectUniquesRegexp.MatchString(pathStr) && method == http.MethodGet {
		return true
	}

	// get topo graph
	if types.TopoGraphicsSearchRegexp.MatchString(pathStr) && method == http.MethodPost {
		return true
	}

	// get object info
	if pathStr == types.BK_TOPO_SEARCH_OBJECTS && method == http.MethodPost {
		return true
	}

	// search object return true
	if types.ObjectPatternRegexp.MatchString(pathStr) {
		return true
	}

	// biz  search privilege, return true
	if strings.Contains(pathStr, types.BK_FIND) {
		return true
	}
	if strings.Contains(pathStr, types.BK_APP_SEARCH) || strings.Contains(pathStr, types.BK_SET_SEARCH) || strings.Contains(pathStr, types.BK_MODULE_SEARCH) || strings.Contains(pathStr, types.BK_INST_SEARCH) || strings.Contains(pathStr, types.BK_HOSTS_SEARCH) {
		return true
	}
	if strings.Contains(pathStr, types.BK_HOSTS_SNAP) || strings.Contains(pathStr, types.BK_HOSTS_HIS) {
		return true
	}
	if strings.Contains(pathStr, types.BK_TOPO_MODEL) {
		return true
	}
	if strings.Contains(pathStr, types.BK_INST_SEARCH_OWNER) && strings.Contains(pathStr, types.BK_OBJECT_PLAT) {
		return true
	}
	if types.SearchPatternRegexp.MatchString(pathStr) {
		return true
	}
	if strings.Contains(pathStr, types.BK_INST_ASSOCIATION_TOPO_SEARCH) {
		return true
	}
	if strings.Contains(pathStr, types.BK_INST_ASSOCIATION_OWNER_SEARCH) {
		return true
	}

	// valid resource config
	if strings.HasSuffix(pathStr, types.ResPattern) || pathStr == types.ImportHosts {
		blog.Debug("valid resource config: %v, rid: %s", pathStr, rid)
		sysPrivi := session.Get("sysPrivi")
		return validSysConfigPrivi(ctx, sysPrivi, types.BK_CC_RESOURCE)

	}

	// valid inst  privilege  op
	if strings.Contains(pathStr, types.BK_INSTS) && !strings.Contains(pathStr, types.BK_TOPO) && !strings.Contains(pathStr, "association") {
		est := c.GetHeader(common.BKAppIDField)
		if "" == est {
			// common inst op valid
			modelPrivi := util.GetStrByInterface(session.Get("modelPrivi"))
			if 0 == len(modelPrivi) {
				blog.Errorf("get model privilege json error, rid: %s", rid)
				return false
			}
			return validModelConfigPrivi(ctx, modelPrivi, method, pathArr)
		} else {
			// mainline inst op valid
			var objName string
			var mainLineObjIDArr []string
			if method == common.HTTPCreate {
				objName = pathArr[len(pathArr)-1]
			} else {
				objName = pathArr[len(pathArr)-2]
			}

			mainLineObjIDStr := util.GetStrByInterface(session.Get("mainLineObjID"))
			err := json.Unmarshal([]byte(mainLineObjIDStr), &mainLineObjIDArr)
			if nil != err {
				blog.Errorf("get main line object id array false, rid: %s", rid)
				return false
			}
			if util.InStrArr(mainLineObjIDArr, objName) {
				// goo main line common object valid
				goto appvalid
			}

		}

		blog.Errorf("valid inst error, rid: %s", rid)
		return false

	}

	// valid inst import privilege
	if strings.Contains(pathStr, types.BK_INSTSI) && !strings.Contains(pathStr, types.BK_IMPORT) {
		est := c.GetHeader(common.BKAppIDField)
		if "" == est {
			modelPrivi := util.GetStrByInterface(session.Get("modelPrivi"))
			if 0 == len(modelPrivi) {
				blog.Errorf("get model privilege json error, rid: %s", rid)
				return false
			}
			return validInstsOpPrivi(ctx, modelPrivi, method, pathArr)
		}
		blog.Errorf("valid inst error, rid: %s", rid)
		return false

	}

	if len(pathArr) > 3 {
		// valid system config exclude resource
		path3 := pathArr[3]
		if util.InArray(path3, types.BK_CC_MODEL_PRE) {
			// only admin config model privilege
			if "1" == role {
				return true
			} else {
				return false
			}
		}
		if util.InArray(path3, types.BK_CC_EVENT_PRE) {
			// valid event config privilege
			sysPrivi := session.Get("sysPrivi")
			return validSysConfigPrivi(ctx, sysPrivi, types.BK_CC_EVENT)
		}
		if util.InArray(path3, types.BK_CC_AUDIT_PRE) {
			// valid event config privilege
			return true
			//			sysPrivi := session.Get("sysPrivi")
			//			return validSysConfigPrivi(sysPrivi, BK_CC_AUDIT)
		}

	}

	// valid biz operation privilege
appvalid:
	return validAppConfigPrivi(c, method, pathStr)

}
